<template>
  <div class="three_load" v-loading="percentageBool" :element-loading-text="nocontainer">
    <div :id="domName">
      <div
        v-if="noDataBool"
        class="noData"
        style="
          font-size: 14rem;
          color: #767676;
          position: absolute;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
        "
      >
        {{ $t('orthodonticReport.orthodonticReport_19') }}
      </div>
      <div class="model_title">{{ modelTitle }}</div>
    </div>
  </div>
</template>

<script>
import * as THREE from 'three';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
import { ArcballControls } from '@/modelTransformation/ArcballControls';

import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
// 顶点着色器
const vertexShader = `
                uniform bool useBlzColor;
                uniform vec3 lightPosition;  
                varying vec3 vNormal;
                varying vec3 eyeDirection;
                varying vec3 lightDirection;
                varying vec3 vColor;
                void main() {
                  if(useBlzColor){
                    vColor=vec3(color[0]/255.0,color[1]/255.0,color[2]/255.0);
                  }else{
                    vColor = color;
                  }
                  vNormal = normalize(modelMatrix * vec4(normal, 0.0)).xyz;
                      gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
                      // vec3 worldPosition = (viewMatrix * modelMatrix * vec4(position, 1.0)).xyz;
                      vec3 worldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
                      eyeDirection = normalize(lightPosition - worldPosition);  
                      lightDirection = normalize(lightPosition - worldPosition);
                }
                `;

// 片段着色器
const fragmentShader = `
                  uniform vec3 lightColor;
                  uniform float ambientStrength;
                  uniform float diffcoef;
                  uniform float specularStrength;
                  uniform vec3 materialColor;
                  uniform vec3 objColor;

                  varying vec3 lightDirection;
                  varying vec3 vNormal;
                  varying vec3 eyeDirection;
                  varying vec3 vColor;
                  uniform bool useBlzColor;
             

                  void main() {
                 
                    vec3 ambient = ambientStrength * lightColor;
                    //漫反射光成分 此时需要光线方向为指向光源方向
                    vec3 n = normalize(vNormal);
                    vec3 l = normalize(lightDirection);
                    float diffFactor = dot(n,l);
                    vec3 diffuse;
                   
                    if(diffFactor < 0.0){
                      diffuse = 0.7  * diffFactor * lightColor;
                    }else{
                      diffuse = diffcoef * diffFactor * lightColor;
                    }
                   
                    
                    //镜面反射成分 此时需要光线方向为由光源指出
                    vec3 reflectDir = normalize(reflect(-l,n));
                    vec3 E = normalize(eyeDirection);
                    float specFactor = pow(max(dot(reflectDir,E),0.0),128.0);
                    vec3 specular = specularStrength * specFactor * lightColor;
                   

                    vec3 result = (ambient + diffuse + specular) * vColor;
                    if(!useBlzColor){
                      result = pow(result, vec3(1.0 / 2.0));
                    }
                    gl_FragColor = vec4(result, 1.0);

                  }
                  `;

export default {
  name: 'three_load',
  data() {
    return {
      scene: new THREE.Scene(),
      controls: null,
      percentageBool: true,
      fileListArr: [],
      nocontainer: this.$t('orthodonticReport.orthodonticReport_20'),
      camera: null, // 相机
      renderer: null, // 渲染器
      mesh: null, // 网格
      point: null,
      groups: [],
      groups1: [],

      group: {},
      group1: {},

      uploadPercentage: 0,
      planGroup: {},
      isModel: true,
      isAnimating: true,
      animationFrameId: '',
      noDataBool: false,
    };
  },
  props: {
    fileList: {
      type: Array,
    },
    domName: {
      type: String,
    },
    modelTitle: {
      type: String,
    },
  },

  mounted() {
    this.clearModel();
    // this.handleFile();
  },

  methods: {
    resetControls() {
      this.controls.reset();
    },
    clearModel() {
      if (this.scene !== null && this.scene.children.length > 0) {
        this.scene.children.pop();
        // 必须要清空当前div下的canvas不然canvas会继续叠加
        const domDiv = document.getElementById(this.domName);
        if (!domDiv) {
          domDiv.removeChild(domDiv.firstChild);
        }
        // ...重置相关data变量，例如camera，light...
        this.scene = null;
        this.controls = null;
        this.camera = null; // 相机
        this.renderer = null; // 渲染器
        this.mesh = null; // 网格
      }
    },
    initModel() {
      this.group.rotateY(-89.5);
      this.group.rotateX(-165);

      this.group1.rotateY(-89.5);
      this.group1.rotateX(-165);
    },
    handleFile() {
      this.groups = [];
      console.log('this.fileList', this.fileList);
      let promises = this.fileList.map((item) => {
        if (!item.url) {
          this.noDataBool = true;
          return (this.percentageBool = false);
        }
        return new Promise((resolve, reject) => {
          this.noDataBool = false;
          if (item.url && item.url.includes('.stl')) {
            this.loadSTL(item.url, item.typeName, resolve); // 加载STL模型
          } else if (item.url && item.url.includes('.ply')) {
            this.loadPLY(item.url, item.typeName, resolve); // 加载PLY模型
          } else if (item.url && item.url.includes('.obj')) {
            this.loadOBJ(false, item.url, item.typeName, resolve);
            this.loadOBJ(true, item.url, item.typeName, resolve);
          } else if (item.url && item.url.includes('.blzmesh')) {
            Promise.all([
              this.loadBlzmesh(false, item.url, item.typeName),
              this.loadBlzmesh(true, item.url, item.typeName),
            ])
              .then(() => resolve()) // 都完成后调用 resolve
              .catch(reject); // 如果有任何一个出错，直接调用 reject
          }
        });
      });

      this.group = new THREE.Object3D();
      this.group1 = new THREE.Object3D();

      // 使用 Promise.all 确保所有模型都加载完毕
      Promise.all(promises)
        .then(() => {
          this.groups.forEach((item) => {
            let tempGroup = new THREE.Group();
            tempGroup.add(item);
            tempGroup['typeName'] = item['typeName'];
            this.group.add(tempGroup);
          });

          this.groups1.forEach((item) => {
            let tempGroup = new THREE.Group();
            tempGroup.add(item);
            tempGroup['typeName'] = item['typeName'];
            this.group1.add(tempGroup);
          });

          // this.group1 = this.group.clone();
          this.scene.add(this.group);
          this.scene.add(this.group1);
          this.group.visible = true;
          this.group1.visible = false;

          this.initModel();

          // // 计算组的中心位置
          let boundingBox = new THREE.Box3().setFromObject(this.group);
          let center = new THREE.Vector3();
          boundingBox.getCenter(center);
          // // 将组居中
          this.group.position.sub(center);

          let boundingBox1 = new THREE.Box3().setFromObject(this.group1);
          let center1 = new THREE.Vector3();
          boundingBox1.getCenter(center1);
          // // 将组居中
          this.group1.position.sub(center1);

          this.nocontainer = this.$t('orthodonticReport.orthodonticReport_20') + '100%';
          // 所有模型加载完成后执行的代码
          this.percentageBool = false;

          let container = document.getElementById(this.domName);

          let canvas = container.querySelector('canvas');
          if (canvas) {
            canvas.style.borderRadius = '10px';
          }
        })
        .catch((err) => {
          this.percentageBool = false;
          this.noDataBool = true;
        });
      this.init();
    },

    // 加载STL模型
    loadSTL(url, typeName, resolve) {
      // const THIS = this
      const loader = new STLLoader();
      loader.load(
        url,
        (geometry) => {
          // 创建材质
          const material = new THREE.MeshLambertMaterial({ color: 0xffffff });
          material.color.setStyle('#b0b0b0'); //模型颜色
          material.side = THREE.DoubleSide;
          this.mesh = new THREE.Mesh(geometry, material);
          this.mesh.rotation.x = -0.5 * Math.PI;
          this.mesh.scale.set(1.2, 1.2, 1.2);
          this[typeName] = this.mesh;
          this.mesh['typeName'] = typeName;
          this.groups.push(this.mesh);
          resolve();
        },
        (event) => {
          this.calculateProgress(typeName, event);
        },
        (onerror) => {
          console.log('err---STL-------->', onerror); // 报错信息在这一行代码
        },
      );
    },
    // 加载PLY模型  ./../../public/jawDie_0_1.ply
    loadPLY(url, typeName, resolve) {
      const loader = new PLYLoader();
      loader.load(
        url,
        (geometry) => {
          // 创建材质
          // const material = new THREE.MeshStandardMaterial({
          //   color: 0xffffff,
          //   flatShading: true,
          // });
          this.shaderMaterial = new THREE.ShaderMaterial({
            uniforms: {
              lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
              ambientStrength: { value: 0.7 },
              diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
              specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）

              // uTexture: { value: new THREE.Color(0xffff00) },
              lightPosition: {
                value: this.camera.position,
              },
            },
            vertexShader: vertexShader,
            fragmentShader: fragmentShader,
            side: THREE.DoubleSide, // 设置材质为双面
            transparent: true,
            vertexColors: true,
          });

          this.mesh = new THREE.Mesh(geometry, this.shaderMaterial);

          this[typeName] = this.mesh;
          this.mesh['typeName'] = typeName;
          this.groups.push(this.mesh);
          resolve();
        },
        (event) => {
          this.calculateProgress(typeName, event);
        },
        (onerror) => {
          console.log('err----------->', onerror); // 报错信息在这一行代码
        },
      );
    },

    // 加载OBJ模型(bool确定是否是平铺)
    loadOBJ(bool, url, typeName, resolve) {
      const objLoader = new OBJLoader();

      objLoader.load(
        url,
        (geometry) => {
          // 如果是平铺的话让上颚夏尔位置一样
          if (bool) {
            // 获取模型的包围盒
            const box = new THREE.Box3().setFromObject(geometry);
            // 计算模型的中心位置
            const center = new THREE.Vector3();
            box.getCenter(center);

            geometry.position.copy(center).multiplyScalar(-1);
          }
          this.shaderMaterial = new THREE.ShaderMaterial({
            uniforms: {
              lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
              ambientStrength: { value: 0.7 },
              diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
              specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）

              // uTexture: { value: new THREE.Color(0xffff00) },
              lightPosition: {
                value: this.camera.position,
              },
            },
            vertexShader: vertexShader,
            fragmentShader: fragmentShader,
            side: THREE.DoubleSide, // 设置材质为双面
            transparent: true,
            vertexColors: true,
          });

          geometry['typeName'] = typeName;

          this[typeName] = geometry;
          this[typeName].traverse((node) => {
            if (node.material) {
              if (node.geometry instanceof THREE.BufferGeometry) {
                node.geometry.computeVertexNormals();
                node.geometry.attributes.normal.needsUpdate = true;
              }
              node.material = this.shaderMaterial;

              node.name = 'obj';
            }
            // if (node instanceof THREE.Mesh && node.material.map) {
            //   node.material.map.encoding = THREE.LinearEncoding;
            // }

            // node.name = 'myCube'; // 设置名称
          });
          if (bool) {
            this.groups1.push(geometry);
          } else {
            this.groups.push(geometry);
          }
          resolve();
        },
        (event) => {
          this.calculateProgress(typeName, event);
        },
        (onerror) => {
          console.log('err----------->', onerror);
        },
      );
    },

    // 加载blz模型
    async loadBlzmesh(bool, url, typeName, resolve) {
      return new Promise(async (resolve, reject) => {
        try {
          // 发起 HTTP 请求
          let res = await this.$http({
            method: 'get',
            url: url,
            withCredentials: false,
            responseType: 'arraybuffer',
          });

          // 处理返回的数据
          const data = await this.handleBlzmeshData(res.data);
          const mesh = data.mesh;
          mesh['typeName'] = typeName;

          // 根据 bool 的值选择将 mesh 添加到不同的组中
          if (bool) {
            this.groups1.push(mesh);
          } else {
            this.groups.push(mesh);
          }
          this.isModel = true;
          resolve(); // 加载成功，调用 resolve
        } catch (error) {
          this.isModel = false;
          reject(error); // 如果有错误，调用 reject
        }
      });
    },
    async handleBlzmeshData(arrayBuffer) {
      // 解析 blzmesh 文件数据
      const dataView = new DataView(arrayBuffer);

      let offset = 0;

      // 读取并验证文件标识符 "blz-2.0"
      const identifier = new TextDecoder().decode(new Uint8Array(arrayBuffer, 0, 7));
      if (identifier !== 'blz-2.0') {
        console.error('文件标识符不正确');
        return false;
      }
      offset += 7;

      // 读取缓存数据大小和图像数据大小
      const bufferSize = Number(dataView.getBigInt64(offset, true));
      offset += 8;
      const imgBufferSize = Number(dataView.getBigInt64(offset, true));
      offset += 8;

      // 提取网格数据
      const meshBuffer = arrayBuffer.slice(offset, offset + bufferSize);
      offset += bufferSize;

      // 提取图像数据(如果存在)
      // 解码网格数据
      try {
        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath('https://oss.blzdentalcloud.com/threejs/');

        // 使用 Promise 包装 Draco 解码过程
        let geometry = await new Promise((resolve, reject) => {
          dracoLoader.decodeDracoFile(
            meshBuffer,
            (geometry) => {
              resolve(geometry);
            },
            null,
            null,
            reject,
          );
        });
        // 计算法线
        geometry.computeVertexNormals();
        geometry.attributes.normal.needsUpdate = true;
        //   // 确保去重后的顶点更新了 UV 信息
        geometry.attributes.uv.needsUpdate = true;
        geometry.attributes.position.needsUpdate = true;
        this.shaderMaterial = new THREE.ShaderMaterial({
          uniforms: {
            lightColor: { value: new THREE.Color(0xffffff) }, // 光源颜色（白色）
            ambientStrength: { value: 0.7 },
            diffcoef: { value: 0.4 }, // 调大可让漫反射更明显（接近真实牙齿）
            specularStrength: { value: 0.1 }, // 镜面反射强度（牙齿高光不宜过强）
            useBlzColor: { value: true },
            lightPosition: {
              value: this.camera.position,
            },
          },
          vertexShader: vertexShader,
          fragmentShader: fragmentShader,
          side: THREE.DoubleSide, // 设置材质为双面
          transparent: true,
          vertexColors: true,
        });

        const mesh = new THREE.Mesh(geometry, this.shaderMaterial);

        return {
          mesh,
          geometry,
        };
      } catch (error) {
        console.error('网格解码失败:', error);
        return null;
      }
    },
    async loadTexture(imgBuffer) {
      const blob = new Blob([imgBuffer], { type: 'image/png' });
      const url = URL.createObjectURL(blob);

      return new Promise((resolve, reject) => {
        const texture = new THREE.TextureLoader().load(
          url,
          () => {
            resolve(texture);
          },
          undefined,
          (error) => {
            reject(error);
          },
        );
      });
    },
    modelTiling() {
      this.group.visible = false;
      this.group1.visible = true;

      this.group1.children.forEach((item) => {
        // 获取模型的包围盒
        // const box = new THREE.Box3().setFromObject(item);
        // // 计算模型的中心位置
        // const center1 = new THREE.Vector3();
        // box.getCenter(center1);

        // item.position.copy(center1).multiplyScalar(-1);

        if (item.typeName.indexOf('upper') !== -1) {
          // 上颚

          item.rotateY(23.5);
          // item.position.set(item.position.x - 40, item.position.y, item.position.z + 44);
          item.position.set(item.position.x - 40, item.position.y, item.position.z + 24);
        } else {
          item.rotateY(-23.5);
          // item.position.set(item.position.x - 20, item.position.y, item.position.z - 20);
          item.position.set(item.position.x - 20, item.position.y, item.position.z - 25);
        }
      });
    },
    // 定义一个方法来计算模型加载进度
    calculateProgress(typeName, event) {
      let plan = 0;
      if (this.planGroup.hasOwnProperty(typeName)) {
        this.planGroup[typeName] = event.loaded / event.total;
      } else {
        this.planGroup[typeName] = '';
      }
      for (let key in this.planGroup) {
        plan += Number(this.planGroup[key]);
      }
      this.uploadPercentage = ((plan / this.fileListArr.length) * 100).toFixed(2);
      if (Number(this.uploadPercentage) > 99) {
        this.uploadPercentage = 99;
      }

      this.nocontainer =
        this.$t('orthodonticReport.orthodonticReport_20') + this.uploadPercentage + '%';
    },

    init() {
      if (this.scene == null || this.scene.children.length < 3) {
        this.scene = new THREE.Scene();
        this.createLight(); // 创建光源
      }
      this.createCamera(); // 创建相机
      this.createRender(); // 创建渲染器
      this.createControls(); // 创建控件对象
      this.render(); // 渲染
    },
    // 创建光源
    createLight() {
      // // 环境光
      var ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
      this.scene.add(ambientLight);
      // // 镜面光
      // this.spotLight = new THREE.SpotLight(0xffffff, 1);
      // scene.add(this.spotLight);
      // 点光源
      this.point = new THREE.PointLight(0xffffff);
      this.point.position.set(0, 0, 200); // 点光源位置
      this.scene.add(this.point);

      //  // 创建坐标轴对象
      // var axes = new THREE.AxesHelper(120);
      // //将坐标轴添加进场景
      // this.scene.add(axes);
    },
    createCamera() {
      const element = document.getElementById(this.domName);
      const width = element.clientWidth; // 窗口宽度
      const height = element.clientHeight; // 窗口高度
      const k = width / height; //窗口宽高比
      const s = 50; //三维场景显示范围控制系数，系数越大，显示的范围越大
      // this.camera = new THREE.PerspectiveCamera(35, k, 0.1, 1000);
      this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 0.1, 1000);
      this.camera.position.set(0, 0, 200);
      // this.camera.position.set(0, 0, 0);
      this.camera.lookAt(new THREE.Vector3(0, 0, 0));
      this.camera.autoUpdate = true;
    },
    onBeforeRender() {},
    // 创建渲染器
    createRender() {
      const element = document.getElementById(this.domName);
      this.renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: false,
        preserveDrawingBuffer: true,
        powerPreference: 'high-performance',
      });
      this.renderer.setPixelRatio(window.devicePixelRatio); // 使用设备原生像素比
      // this.renderer.outputEncoding = THREE.sRGBEncoding;
      this.renderer.outputEncoding = THREE.LinearEncoding;
      this.renderer.setSize(element.clientWidth, element.clientHeight); // 设置渲染区域尺寸
      this.renderer.setClearColor('rgb(255,255,255)', 0.5); // 设置背景颜色

      element.appendChild(this.renderer.domElement);
    },

    // 创建控件对象
    createControls() {
      this.controls = new ArcballControls(this.camera, this.renderer.domElement, this.scene);
      this.controls.setGizmosVisible(false);
      this.controls.enablePan = false;
      this.controls.dampingFactor = 100;
      this.controls.unsetMouseAction('FOV', 0);
      this.controls.setMouseAction('ZOOM', 1);
      this.controls.setMouseAction('ROTATE', 2);
      this.controls.unsetMouseAction('0');
      this.controls.target = new THREE.Vector3(0, 0, 0);
      this.controls.addEventListener('change', () => {
        this.$emit('syncChange');
      });
    },
    render() {
      this.animationFrameId = requestAnimationFrame(this.render);
      if (!this.isAnimating) {
        return false;
      }
      if (this.controls) {
        this.controls.update();
      }
      if (this.camera.position) {
        var vector = this.camera.position.clone();
      }
      // this.camera.updateMatrix(); // 更新相机的世界矩阵
      // this.camera.updateMatrixWorld(); // 更新相机的世界矩阵
      this.point.position.set(vector.x, vector.y, vector.z); // 点光源位置
      // this.$emit('syncChange');

      this.renderer.render(this.scene, this.camera);
    },
  },
  beforeDestroy() {
    // 清理渲染器
    if (this.renderer) {
      this.renderer.dispose();
    }

    // 清理场景中的对象
    this.scene.traverse((object) => {
      if (object.geometry) object.geometry.dispose();
      if (object.material) {
        if (object.material.map) object.material.map.dispose();
        object.material.dispose();
      }
    });

    // 取消动画循环
    cancelAnimationFrame(this.animationFrameId);
  },
};
</script>

<style lang="scss" scoped>
.three_load {
  width: 100%;
  height: 100%;
  border-radius: 10px;

  ::v-deep .el-loading-mask {
    border-radius: 10px;
  }

  > div {
    width: 100%;
    height: 100%;
    position: relative;

    .model_title {
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translate(-50%, -50%);
      font-size: 22px;
    }
  }
}
</style>
